#
# ComicBrowser.py
# (c) 2008 Will Baker.
#

from PyQt4.QtCore import Qt, QRect, QSize, QPoint, SIGNAL
from PyQt4.QtGui import QWidget, QPainter

from model import ComicLoader

from BrowserItem import BrowserItem

class ComicBrowser(QWidget):
    """
    A widget for viewing a collection of Comics.
    """
    def __init__( self, parent=None ):
        QWidget.__init__( self, parent )
        self.page = 0
        self.items = []
        self.margin = 15
        self.setMouseTracking( True )
        self.active_item = None
        
    def set_results( self, results ):
        """
        Set the results currently being viewed.
        """
        ComicLoader.clear()
        
        for i in self.items:
            i.clear()
        
        self.items = []
        
        for r in results:
            item = BrowserItem( r.item, self )
            self.items.append( item )
        
        self.page = 0
        self.apply_browser_item_layout()
        self.update()
        
    def begin_page_update( self ):
        """
        Called just before a page is updated.
        """
        ComicLoader.clear()
        (f, t) = self.find_item_range()
        for i in range(f, t):
            self.items[i].loading = False
        
    def end_page_update( self ):
        """
        Called just after a page is updated.
        """
        self.page = min( self.page, self.find_page_count()-1 )
        self.apply_browser_item_layout()
        self.update()
        
    def on_previous_page( self ):
        """
        Go to the previous page.
        """
        if self.page > 0:
            self.begin_page_update()
            self.page -= 1
            self.end_page_update()
        
    def on_next_page( self ):
        """
        Go to the next page.
        """
        if self.page < self.find_page_count()-1:
            self.begin_page_update()
            self.page += 1
            self.end_page_update()
            
    def clear( self ):
        """
        Stop all BrowserItems from loading previews.
        """
        for i in self.items:
            i.clear()
        
    def apply_browser_item_layout( self ):
        """
        Apply the layout positions to each of the BrowserItems. This needs to occur whenever
        the BrowserItems change or this widget is resized.
        """
        (items_per_row, items_per_col, items_per_page) = self.find_layout_information()
        (f, t) = self.find_item_range()
        
        items_width = min(items_per_row, len(self.items)) * (BrowserItem.size.width() + self.margin) + self.margin
        offset = self.width() / 2 - items_width / 2
        
        x = self.margin
        y = self.margin
        r = 0
        for i in range( f, t ):
            self.items[i].x = x + offset
            self.items[i].y = y
        
            x += BrowserItem.size.width() + self.margin
            r += 1
            
            if r == items_per_row:
                r = 0
                x = self.margin
                y += BrowserItem.size.height() + self.margin
                
    def find_layout_information( self ):
        """
        Find the number of items per row, column and page.
        """
        items_per_row = int(self.width() / (BrowserItem.size.width() + self.margin))
        items_per_col = int(self.height() / (BrowserItem.size.height() + self.margin))
        items_per_page = min(items_per_row * items_per_col, len(self.items))
        
        return (items_per_row, items_per_col, items_per_page)
        
    def find_item_range( self ):
        """
        Find the minimum and maximum visible pages.
        """
        (_, _, items_per_page) = self.find_layout_information()
        f = min( self.page * items_per_page, len(self.items) )
        t = min( f + items_per_page, len(self.items) )
        
        return (f, t)
        
    def find_page_count( self ):
        """
        Find the number of pages.
        """
        (_, _, items_per_page) = self.find_layout_information()
        if items_per_page > 0:
            return int( round(len(self.items) / float(items_per_page)) )
        else:
            return 0
        
    def pick_item( self, position ):
        """
        Find an item at the given position.
        """
        (f, t) = self.find_item_range()
        
        for i in range(f, t):
            if self.items[i].rect().contains( position ):
                return self.items[i]
                
        return None
        
    def mouseMoveEvent( self, e ):
        """
        Handle a mouse movement event.
        """
        item = self.pick_item( e.pos() )

        if item != self.active_item:
            if item != None:
                item.on_enter()
                self.emit( SIGNAL("item_entered"), item )
            if self.active_item != None:
                self.active_item.on_leave()
        self.active_item = item
        
    def mousePressEvent( self, e ):
        """
        Handle a mouse press event.
        """
        item = self.pick_item( e.pos() )
        
        if item != None:
            self.emit( SIGNAL("item_selected"), item )
        
    def resizeEvent( self, e ):
        """
        Handle a resize event - this lays out the BrowserItems again.
        """
        self.begin_page_update()
        self.end_page_update()
        
    def paintEvent( self, e ):
        """
        Handle a paint event.
        """
        painter = QPainter( self )
        painter.fillRect( self.rect(), Qt.white )
        
        (f, t) = self.find_item_range()
        
        for i in range(f, t):
            self.items[i].render( painter )
        
        painter.end()
